<?php
namespace aop\core;

trait AbstractConcretOperationTrait {

    private $_abstract_binding_map = [];

    private function _fetchClassnameAndConcret($abstract = '', $concret = NULL) {
        if ($concret instanceof $abstract) {
            return $this->_makeByAbstractAndConcret($abstract, $concret);
        }

        if (is_string($abstract) && $abstract && !is_object($concret)) {
            return $this->_makeByAbstract($abstract);
        }

        if (is_object($concret)) {
            return $this->_makeByConcret($concret);
        }

        throw new \Exception('invalid params');
    }

    private function _makeByAbstractAndConcret($abstract, $concret) {
        $this->_abstract_binding_map[$abstract] = $concret;
        return ['class_name' => $abstract, 'instance' => $concret];
    }

    private function _makeByAbstract($abstract) {
        if (isset($this->_abstract_binding_map[$abstract])) {
            return $this->_makeByAbstractAndConcret($abstract, $this->_abstract_binding_map[$abstract]);
        }

        $classReflection = new \ReflectionClass($abstract);
        $concret = $classReflection->isInstantiable() ? $classReflection->newInstance() : $classReflection->newInstanceWithoutConstructor();

        if ($classReflection->hasMethod('init')) {
            (function(){
                $this->init();
            })->call($concret);
        }

        return $this->_makeByAbstractAndConcret($abstract, $concret);
    }

    private function _makeByConcret($concret) {
        return ['class_name' => '', 'instance' => $concret];
    }
}